import gurobipy as gp
from gurobipy import *
from gurobipy import GRB
import numpy as np
list_yewu = []
global k


u = 0   #用来表示业务号
for i in range(4, 9):                                                  #  i:端节点
    if i <= 5:                                                          #与交换机1-11相连接的端结点
        p = i - 3                                                       #经过的第一个交换机
        for j in range(1, 4):                                           #经过的第二个交换机
            if j == p:                                                   #自己不给自己发业务
                continue
            else:
                if j == 3:                                              #经过交换机12转接的业务
                    for k in range(6, 9):
                        list_yewu.append([])
                        list_yewu[u].append(u)
                        list_yewu[u].append(i)
                        list_yewu[u].append(p)
                        list_yewu[u].append(j)
                        list_yewu[u].append(k)
                        u = u + 1
                else:      #不经过交换机3的业务
                    y = j + 3
                    list_yewu.append([])
                    list_yewu[u].append(u)
                    list_yewu[u].append(i)
                    list_yewu[u].append(p)
                    list_yewu[u].append(j)
                    list_yewu[u].append(y)
                    u = u + 1
    else:    #与交换机3相连接的端结点
        z = 3
        for k in range(4, 9):
            if k <= 5:
                list_yewu.append([])
                list_yewu[u].append(u)
                list_yewu[u].append(i)
                list_yewu[u].append(z)
                list_yewu[u].append(k - 3)
                list_yewu[u].append(k)
                u = u + 1
            else:
                if k == i:
                    continue
                else:
                    list_yewu.append([])
                    list_yewu[u].append(u)
                    list_yewu[u].append(i)
                    list_yewu[u].append(z)
                    list_yewu[u].append(k)
                    u = u + 1
print(list_yewu)
list_lianlu = [[]]
list_fz = []
list_fz.append(list_yewu[0][1])
list_fz.append(list_yewu[0][2])
list_lianlu[0].append(list_yewu[0][1])
list_lianlu[0].append(list_yewu[0][2])
# print(f'lianlu:',list_lianlu)
# print(f'fz:',list_fz)
for i in range(0, len(list_yewu)):
    for j in range(1, len(list_yewu[i]) - 1):
        for k in range(0, len(list_fz), 2):
            if list_fz[k] == list_yewu[i][j] and list_fz[k + 1] == list_yewu[i][j + 1]:
                break
            else:
                if k < len(list_fz) - 2:
                    continue
                else:
                    list_fz.append(list_yewu[i][j])
                    list_fz.append(list_yewu[i][j + 1])
                    list_lianlu.append([])
                    list_lianlu[len(list_lianlu) - 1].append(list_yewu[i][j])
                    list_lianlu[len(list_lianlu) - 1].append(list_yewu[i][j + 1])
for i in range(0, len(list_yewu)):
    for j in range(1, len(list_yewu[i]) - 1):
        for k in range(0, len(list_fz), 2):
            if list_fz[k] == list_yewu[i][j] and list_fz[k + 1] == list_yewu[i][j + 1]:
                list_lianlu[int(k / 2)].append(list_yewu[i][0])
                break

# 目前位置，list_lianlu中前两列存放了链路元素
# list_fz存放了一维的链路，两个一跳
# 现在需要做的工作就是开始对整个业务表进行遍历了
print('一条链路下:',list_lianlu)
print('不同业务所走的路径(第一个元素为业务号):',list_yewu)

# 　设置随机种子，保证每次生成的随机数一样，可以不设置（去除下面一行代码，将所有的 rd 替换成 np.random 即可）
rd = np.random.RandomState(888)
a = rd.randint(1, 4, size=72)

try:

    # ①创建一个模型,gurobipy.Model('模型名')
    m = gp.Model("TTE_test")

    # ②设置参数变量
    C = m.addVars(range(0, 20), range(1, 9), vtype='C', name="C")

    M = m.addVar(lb=0, ub=gurobipy.GRB.INFINITY, vtype=GRB.SEMIINT, name='M')
    '''相当于一下设置了下面Z1-Z7，用Z[],表示下标'''
    Z = m.addVars(73, vtype=GRB.BINARY, name='Z')
    m.ModelSense = GRB.MINIMIZE

    '''③设置目标函数'''

    '''首先定义一个线性表达式'''
    obj1 = LinExpr(0)
    for i in range(len(list_yewu)):
        for j in range(len(list_yewu[i])):
            if j == len(list_yewu[i])-2:
                #将目标函数系数与决策变量相乘，并进行连加
                obj1.addTerms(1, C[list_yewu[i][0], list_yewu[i][j] ] ) #1为系数,此处以后可能会被修改
    '''将表示目标函数的线性表达式加入模型'''
    m.setObjectiveN(obj1, index=0, priority=2, abstol=0, reltol=0, name='obj1')

    obj2 = LinExpr(0) #让初始发送时间尽可能小
    for i in range(len(list_yewu)):
            obj2.addTerms(1, C[list_yewu[i][0], list_yewu[i][1]])
    m.setObjectiveN(obj2, index=1,priority=1, abstol=0, reltol=0, name='obj2')


    '''创建结点约束条件'''
    k = 0
    for i in range(0, len(list_lianlu)):
        for j in range(2, len(list_lianlu[i]) - 1):
            for u in range(j + 1, len(list_lianlu[i])):
                k = k + 1
                m.addConstr(C[list_lianlu[i][j], list_lianlu[i][0]] + a[k-1] <= C[list_lianlu[i][u], list_lianlu[i][0]] + Z[k] * M,'')
                m.addConstr(C[list_lianlu[i][u], list_lianlu[i][0]] + a[k-1] <= C[list_lianlu[i][j], list_lianlu[i][0]] + (1 - Z[k]) * M, '')

    '''创建链路约束条件 '''
    for i in range(0, len(list_yewu)):
        for j in range(1, len(list_yewu[i]) - 2):
            m.addConstr(C[i, list_yewu[i][j]] + 1 <= C[i, list_yewu[i][j + 1]])


    m.write('Test_5_point.lp')

    '''启动NoRel启发式算法'''
    m.Params.NoRelHeurtime = 18000
    m.Params.MIPFocus = 1
    # env0 = m.getMultiobjEnv(0)
    # env1 = m.getMultiobjEnv(1)
    # env0.setParam('TimeLimit', 6000)
    # env1.setParam('TimeLimit', 3000)

    #⑤开始求解
    m.optimize()
    m.discardMultiobjEnvs()

    '''循环遍历你设置的所有变量值'''
    for v in m.getVars():
        print('%s %g' % (v.varName, v.x))

    #  '''一行一下输出所有设置的变量值'''
    v = m.getVars()
    print(v)

    # '''显示设置的一个目标，如果设置了多个目标，返回优先级最大的'''
    # print('Obj1: %g' % m.objVal)  # objVal：（优化后最优的目标结果值）
    #
    # print(m.NumObj) 输出结果为2 ，因为我们设置了两个目标函数, 序号 index=0 ,index=1
    for i in range(m.NumObj):  # 可以使用NumObj属性查询(或修改)模型中的目标数量，它的值是目标函数的数量。遍历它，得到的是一个个index的值
        m.setParam(GRB.Param.ObjNumber, i)
        print('Obj%d = ' % (i + 1), m.ObjNVal)


except gp.GurobiError as e:
    print('Error code ' + str(e.errno) + ': ' + str(e))

except AttributeError:  # 属性错误
    print('Encountered an attribute error')
